home *** CD-ROM | disk | FTP | other *** search
Wrap
/* ** Apple Macintosh Developer Technical Support ** ** Routines demonstrating how to play a sound from disk using SndPlayDoubleBuffer. ** ** by Mark Cookson, Apple Developer Technical Support ** ** File: DoubleBufferFromFile.c ** ** Copyright ©1996 Apple Computer, Inc. ** All rights reserved. ** ** You may incorporate this sample code into your applications without ** restriction, though the sample code has been provided "AS IS" and the ** responsibility for its operation is 100% yours. However, what you are ** not permitted to do is to redistribute the source as "Apple Sample ** Code" after having made changes. If you're going to re-distribute the ** source, we require that you make it clear in the source that the code ** was descended from Apple Sample Code, but that you've made changes. */ #include "DoubleBufferFromFile.h" /* Purpose: This creates a new SoundInfo structure and initializes it by calling the private function ASoundInit. Side Effects: None. */ /*-----------------------------------------------------------------------*/ SoundInfoPtr ASoundNew (OSErr *theErr) /*-----------------------------------------------------------------------*/ { SoundInfoPtr theSoundInfo = nil; *theErr = noErr; theSoundInfo = (SoundInfoPtr)NewPtrClear (sizeof (SoundInfo)); if (MemError () != noErr || theSoundInfo == nil) { *theErr = kInitErr; } else { *theErr = ASoundInit (theSoundInfo); } if (*theErr != noErr) { DebugPrint ("\pError in ASoundNew"); } return theSoundInfo; } /* Purpose: Display a StandardFile dialog to select a file. Opens the file selected by the user. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundGetFileToPlay (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { FileFilterUPP myFilterUPP = nil; OSErr theErr = noErr; short i = 0; Boolean good = false; if (IsValid (theSoundInfo)) { myFilterUPP = NewFileFilterProc (ASoundFileFilter); if ((theSoundInfo->globals.ggestaltStandardFileAttr & gestaltStandardFile58) == false) { StandardFileReply theSFReply; StandardGetFile (myFilterUPP, kNoFirstFiltering, nil, &theSFReply); theSoundInfo->vRefNum = theSFReply.sfFile.vRefNum; if (theSFReply.sfGood == true) { switch (theSFReply.sfType) { case kSNDResource: case kResource: theErr = FSpOpenRF (&theSFReply.sfFile, fsCurPerm, &(theSoundInfo->refNum)); break; case kCompressedAIFFFile: case kUncompressedAIFFFile: case kWAVEFile: case kAUFile: theErr = FSpOpenDF (&theSFReply.sfFile, fsCurPerm, &(theSoundInfo->refNum)); break; default: DebugPrint ("\pASoundGetFileToPlay should have never executed this line"); } if (theErr != noErr) { DebugPrint ("\pCouldn't open file"); } else { theSoundInfo->fileType = theSFReply.sfType; for (i = 0; i <= theSFReply.sfFile.name[0]+1; i++) theSoundInfo->theName[i] = theSFReply.sfFile.name[i]; } } else { theErr = userCanceledErr; } } else { SFReply theSFReply; Point where = {kInit, kInit}; Rect screenRect = GetMainScreenRect(); long dirID = kInit, procID = kInit; where.h = screenRect.right / kHorizAdjust; /* This will put the SFGetFile dialog in the same place */ where.v = screenRect.bottom / kVertAdjust; /* as the StandardGetFile dialog (or really close). */ if ((GetMBarHeight() + kOne) > where.h) { where.h = GetMBarHeight() + kOne; } SFGetFile (where, nil, myFilterUPP, kNoFirstFiltering, nil, nil, &theSFReply); good = theSFReply.good; if (theSFReply.good == true) { theErr = GetWDInfo (theSFReply.vRefNum, &(theSoundInfo->vRefNum), &dirID, &procID); if (theErr == noErr) { switch (theSFReply.fType) { case kSNDResource: case kResource: theErr = HOpenRF (theSoundInfo->vRefNum, dirID, theSFReply.fName, fsCurPerm, &(theSoundInfo->refNum)); break; case kCompressedAIFFFile: case kUncompressedAIFFFile: case kWAVEFile: case kAUFile: theErr = HOpenDF (theSoundInfo->vRefNum, dirID, theSFReply.fName, fsCurPerm, &(theSoundInfo->refNum)); break; default: DebugPrint ("\pASoundGetFileToPlay should have never executed this line"); } if (theErr != noErr) { DebugPrint ("\pCouldn't open file"); } else { theSoundInfo->fileType = theSFReply.fType; for (i = 0; i <= theSFReply.fName[0]+1; i++) theSoundInfo->theName[i] = theSFReply.fName[i]; } } else { theErr = kFileErr; DebugPrint ("\pCouldn't translate working directory"); } } else { theErr = userCanceledErr; } } DisposeRoutineDescriptor (myFilterUPP); } else { theErr = kNilPtrErr; } if (theErr != noErr && theErr != userCanceledErr) { DebugPrint ("\pError in ASoundGetFileToPlay"); } return theErr; } /* Purpose: Checks a file to see if its header can be parsed and the file can be played. This will return an error if the sound will not play, returning noErr means that sound will play. Parsing a header can take some time, this routine is a canidate for speed improvements. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundCanThisPlay (CInfoPBPtr theFileInfo) /*-----------------------------------------------------------------------*/ { SoundInfoPtr theSoundInfo = nil; long dataStart = kInit, sndLength = kInit; OSErr theErr = noErr, closeErr = noErr; if (theFileInfo != nil) { theSoundInfo = ASoundNew (&theErr); if (theErr == noErr) { theSoundInfo->vRefNum = theFileInfo->hFileInfo.ioVRefNum; theSoundInfo->fileType = theFileInfo->hFileInfo.ioFlFndrInfo.fdType; theErr = HOpenDF (theFileInfo->hFileInfo.ioVRefNum, theFileInfo->hFileInfo.ioFlParID, theFileInfo->hFileInfo.ioNamePtr, fsRdPerm, &theSoundInfo->refNum); if (theErr == noErr) { switch (theSoundInfo->fileType) { case kCompressedAIFFFile: case kUncompressedAIFFFile: theErr = ASoundGetAIFFHeader (theSoundInfo, &dataStart, &sndLength); break; case kWAVEFile: theErr = ASoundGetWAVEHeader (theSoundInfo, &dataStart, &sndLength); break; case kAUFile: theErr = ASoundGetULAWHeader (theSoundInfo, &dataStart, &sndLength); break; case kSNDResource: case kResource: theErr = noErr; /* For now, assume it will play */ break; default: /* The file type is not one that we can even parse. */ theErr = kUnknownFormat; break; } } } } else { theErr = kNilPtrErr; } closeErr = FSClose (theSoundInfo->refNum); DisposeRoutineDescriptor (ASoundGetSoundCallBack (theSoundInfo)); DisposePtr ((Ptr)theSoundInfo); if (closeErr != noErr) { theErr = closeErr; } if (theErr != noErr && theErr != kUnknownFormat) { DebugPrint ("\pError in ASoundCanThisPlay"); } return theErr; } /* Purpose: This function is called to get ready to play a sound. Use this if you want to make sure that there is enough memory to play the sound. Side Effects: This will call routines that will allocate memory needed to all of the various structures needed by the Sound Manager and memory to be used as the sounds' buffers. */ /*-----------------------------------------------------------------------*/ OSErr ASoundReadyForPlaying (SoundInfoPtr theSoundInfo, unsigned long bufferSize) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (IsValid (theSoundInfo)) { if (theSoundInfo->globals.gSupportsSPDB == false) { DebugPrint ("\pThis machine doesn't support SoundPlayDoubleBuffer, can't play!"); theErr = kNoSPDBErr; } else { if ((ASoundGetBytesCopied (theSoundInfo) <= kMaxAIFFHeaderSize) && theSoundInfo->paused == false) { /* do we want to start at the begining of the file? */ theSoundInfo->soundDone = false; theErr = SndNewChannel (&(theSoundInfo->chan), sampledSynth, nil, ASoundGetSoundCallBack (theSoundInfo)); if (theErr != noErr) { DebugPrint ("\pSndNewChannel error!"); } else { theErr = SetUpSoundHeader (theSoundInfo, bufferSize); if (theErr == noErr) { theErr = ASoundPrimeBuffers (theSoundInfo); ASoundSetCurBuffer (theSoundInfo, kOne); } } } else { theErr = ASoundPrimeBuffers (theSoundInfo); } } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundReadyForPlaying!"); (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem); } return theErr; } /* Purpose: Call this after you have called ASoundReadyForPlaying to start playing the sound you prepaired. Side Effects: Starts the sound playing. */ /*-----------------------------------------------------------------------*/ OSErr ASoundPlay (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (IsValid (theSoundInfo)) { if (theSoundInfo->playing == false || theSoundInfo->paused == true) { if (theSoundInfo->globals.gSupportsSPDB == false) { theErr = kNoSPDBErr; DebugPrint ("\pThis machine doesn't support SoundPlayDoubleBuffer, can't play!"); } else { if (theSoundInfo->paused == false) { theErr = SndPlayDoubleBuffer (theSoundInfo->chan, (SndDoubleBufferHeaderPtr)&(theSoundInfo->doubleHeader)); if (theErr == noErr) { theErr = InstallCallBack (theSoundInfo); if (theErr == noErr) { theSoundInfo->playing = true; } } } } } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundPlay!"); (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem); } return theErr; } /* Purpose: Wrapper function called to start playing a sound. Use this if you are pretty sure the sound will play, or just don't care specifically what goes wrong. Side Effects: This will call routines that will allocate memory needed for all of the various structures needed by the Sound Manager and memory to be used as the sounds' buffers. */ /*-----------------------------------------------------------------------*/ OSErr ASoundStartPlaying (SoundInfoPtr theSoundInfo, unsigned long bufferSize) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; theErr = ASoundReadyForPlaying (theSoundInfo, bufferSize); if (theErr == noErr) { theErr = ASoundPlay (theSoundInfo); } if (theErr != noErr) { DebugPrint ("\pError in ASoundStartPlaying!"); (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem); } return theErr; } /* Purpose: Stops the currently playing sound. Side Effects: Stopping the currently playing sound will cause the sound completion routine to run. */ /*-----------------------------------------------------------------------*/ OSErr ASoundStop (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {quietCmd, kInit, kInit}; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theErr = SndDoImmediate (theSoundInfo->chan, &theCmd); if (theErr == noErr) { theSoundInfo->playing = false; theSoundInfo->paused = false; } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundStop"); } return theErr; } /* Purpose: Wrapper so the user doesn't have to keep track of if the sound is playing or not. Side Effects: If resuming a sound and the user had also called ASoundPauseForAdjust this will reinstall the sound completion callback. */ /*-----------------------------------------------------------------------*/ OSErr ASoundPause (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { if (theSoundInfo->paused == false) { theErr = PauseSound (theSoundInfo); } else { theErr = ResumeSound (theSoundInfo); } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundPause"); } return theErr; } /* Purpose: If the sound is paused, resume playing. If the sound is playing, pause playing. This differs from ASoundPause because it actually stops the sound instead of pausing it. When the sound is paused for adjusting you can reset where the sound will next start playing from without having to play the remainder of the current buffer. This routine is used for the QuickTime style playing. Side Effects: Removes the callback from the sound channel because otherwise while adjusting the sound the Sound Manager would call our clean up routine. When resuming a sound ASoundStartPlaying will install our callback routine if necessary (if the sound wasn't already paused). */ /*-----------------------------------------------------------------------*/ OSErr ASoundPauseForAdjust (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {kInit, kInit, kInit}; OSErr theErr = noErr; static Fixed oldRate = kInit; if (StrictIsValid (theSoundInfo)) { if (theSoundInfo->adjusting == false) { theCmd.cmd = flushCmd; /* so the sound completion callback doesn't get called */ theErr = SndDoImmediate (theSoundInfo->chan, &theCmd); if (theErr == noErr) { theSoundInfo->adjusting = true; theCmd.cmd = quietCmd; theErr = SndDoImmediate (theSoundInfo->chan, &theCmd); if (theErr == noErr) { theSoundInfo->hasBeenAdjusted = true; theSoundInfo->playing = false; } } } else { theSoundInfo->adjusting = false; ASoundStartPlaying (theSoundInfo, nil); } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundPauseForAdjust"); } return theErr; } /* Purpose: Sound is done playing, dispose of the memory we no longer need. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundDonePlaying (SoundInfoPtr theSoundInfo, unsigned long options) /*-----------------------------------------------------------------------*/ { myParamBlockRec *myPB = nil; OSErr theErr = noErr; short i = kInit, savedVRefNum = kInit, savedRefNum = kInit; if (IsValid (theSoundInfo)) { theSoundInfo->soundDone = false; /* so we don't get called multiple times */ savedVRefNum = theSoundInfo->vRefNum; savedRefNum = theSoundInfo->refNum; if ((options == kCloseFile) || (options > kCloseFile + kFreeMem)) { DebugPrint ("\pInvalid selector passed to ASoundDonePlaying"); theErr = kBadValue; } else { myPB = (myParamBlockRec*)theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufOne]->dbUserInfo[kPBPtr]; if (options > kNoOptions) { if (myPB != nil) { if (options == kCloseFile + kFreeMem) { theErr = FSClose (myPB->pb.ioParam.ioRefNum); } if (theErr == noErr) { for (i = kInit; i <= kOne; i++) { DisposeRoutineDescriptor (((myParamBlockRec*)theSoundInfo->doubleHeader.dbhBufferPtr[i]->dbUserInfo[kPBPtr])->pb.ioParam.ioCompletion); DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]->dbUserInfo[kPBPtr]); /* Have to unhold memory that was held */ UnholdMemory ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i], sizeof(SndDoubleBuffer) + ASoundGetBufferSize (theSoundInfo)); DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]); } } else { DebugPrint ("\pFSClose error!"); } theErr = SndDisposeChannel (theSoundInfo->chan, true); DisposeRoutineDescriptor (ASoundGetSoundCallBack (theSoundInfo)); DisposeRoutineDescriptor (theSoundInfo->doubleHeader.dbhDoubleBack); if (theErr != noErr) { DebugPrint ("\pSndDisposeChannel error!"); } } } if (options == kNoOptions) { ASoundSetBytesCopied (theSoundInfo, theSoundInfo->dataStart); ASoundSetCurBuffer (theSoundInfo, kStart); theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufOne]->dbFlags ^= dbLastBuffer; theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufTwo]->dbFlags ^= dbLastBuffer; } if (options == kFreeMem) { ASoundInit (theSoundInfo); theSoundInfo->vRefNum = savedVRefNum; theSoundInfo->refNum = savedRefNum; } else { if (options == kCloseFile + kFreeMem) { ASoundInit (theSoundInfo); } } } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundDonePlaying"); } return theErr; } /* Purpose: Returns the name of the file containing the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundGetSoundName (SoundInfoPtr theSoundInfo, Str63 theName) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; short i = 0; if (StrictIsValid (theSoundInfo) && theName != nil) { for (i = 0; i <= theSoundInfo->theName[0]+1; i++) theName[i] = theSoundInfo->theName[i]; } return theErr; } /* Purpose: Gets the number of the current buffer (in the range 1 to ASoundGetNumBuffers()) of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ long ASoundGetCurBuffer (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { long returnValue = kInit; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { returnValue = theSoundInfo->currentBuffer; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetCurBuffer"); } return returnValue; } /* Purpose: Sets which buffer should be the next buffer to play from (in the range 1 to ASoundGetNumBuffers()) for the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundSetCurBuffer (SoundInfoPtr theSoundInfo, long newValue) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (IsValid (theSoundInfo)) { if ((newValue >= kStart) && (newValue <= ASoundGetNumBuffers (theSoundInfo))) { theSoundInfo->currentBuffer = newValue; } else { theErr = kBadRange; } } else { theErr = kNilPtrErr; } return theErr; } /* Purpose: Gets the number of buffers that the currently playing sound will need to play in its entirety. Side Effects: None. */ /*-----------------------------------------------------------------------*/ long ASoundGetNumBuffers (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { long returnValue = kInit; OSErr theErr = noErr; if (IsValid (theSoundInfo)) { returnValue = theSoundInfo->numBuffers; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetNumBuffers"); } return returnValue; } /* Purpose: Gets the length (in bytes) of the currently playing sound. This number does not include any header bytes. Side Effects: None. */ /*-----------------------------------------------------------------------*/ long ASoundGetNumTotalBytes (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { long returnValue = kInit; OSErr theErr = noErr; if (IsValid (theSoundInfo)) { returnValue = theSoundInfo->bytesTotal; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetNumTotalBytes"); } return returnValue; } /* Purpose: Gets the number of bytes that will be played by the end of the current buffer of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ long ASoundGetBytesCopied (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { long returnValue = kInit; OSErr theErr = noErr; if (IsValid (theSoundInfo)) { returnValue = theSoundInfo->bytesCopied; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetBytesCopied"); } return returnValue; } /* Purpose: Sets the location in the file where the next buffer should be filled from for the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundSetBytesCopied (SoundInfoPtr theSoundInfo, long newValue) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (IsValid (theSoundInfo)) { if (newValue >= theSoundInfo->dataStart || newValue == kInit) { theSoundInfo->bytesCopied = newValue; } else { theErr = kBadValue; } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundSetBytesCopied"); } return theErr; } /* Purpose: Gets the size of a buffer of the currently playing sound. Multiply by two to know how much memory is reserved for buffering the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ long ASoundGetBufferSize (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { long returnValue = kInit; OSErr theErr = noErr; if (IsValid (theSoundInfo)) { returnValue = theSoundInfo->doubleBufferSize; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetBufferSize"); } return returnValue; } /* Purpose: Gets the UPP for the function that should be called when the currently playing sound finishes. Side Effects: None. */ /*-----------------------------------------------------------------------*/ SndCallBackUPP ASoundGetSoundCallBack (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { SndCallBackUPP returnValue = nil; OSErr theErr = noErr; if (IsValid (theSoundInfo)) { returnValue = theSoundInfo->theSoundCallBackUPP; } else { theErr = kNilPtrErr; returnValue = nil; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetSoundCallBack"); } return returnValue; } /* Purpose: Sets the function that should be called when the the currently playing sound finishes. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundSetSoundCallBack (SoundInfoPtr theSoundInfo, void* newValue) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (IsValid (theSoundInfo)) { if (theSoundInfo->theSoundCallBackUPP != nil) { DisposeRoutineDescriptor (theSoundInfo->theSoundCallBackUPP); } if (newValue != nil) { theSoundInfo->theSoundCallBackUPP = NewSndCallBackProc(newValue); } else { theSoundInfo->theSoundCallBackUPP = NewSndCallBackProc(ASoundDoneCallBack); DebugPrint ("\pDid you really want the default sound callback?"); } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundSetSoundCallBack"); } return theErr; } /* Purpose: Says whether to play the currently playing sound backwards (it actually reverses the sound in the buffer). Side Effects: Takes effect when the next sound buffer gets filled. */ /*-----------------------------------------------------------------------*/ OSErr ASoundPlayBackwards (SoundInfoPtr theSoundInfo, Boolean newValue) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theSoundInfo->backwards = newValue; } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundPlayBackwards"); } return theErr; } /* Purpose: Returns true if the currently playing sound's buffer is set to be reversed. Side Effects: None. */ /*-----------------------------------------------------------------------*/ Boolean ASoundIsBackwards (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; Boolean returnValue = false; if (StrictIsValid (theSoundInfo)) { returnValue = theSoundInfo->backwards; } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundIsBackwards"); } return returnValue; } /* Purpose: Returns true if the sound has finished playing. Side Effects: None. */ /*-----------------------------------------------------------------------*/ Boolean ASoundIsDone (SoundInfoPtr theSoundInfo) /*-----------------------------------------------------------------------*/ { OSErr theErr = noErr; Boolean returnValue = false; if (StrictIsValid (theSoundInfo)) { returnValue = theSoundInfo->soundDone; } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundIsDone"); } return returnValue; } /* Purpose: Changes the volume of the currently playing sound. The values you pass in are added to the current values. Negitive values will decrease the volume, positive values will increase the volume. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundChangeVolume (SoundInfoPtr theSoundInfo, unsigned short leftVol, unsigned short rightVol) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {volumeCmd, kInit, kInit}; OSErr theErr = noErr; unsigned short tempLeft = kInit, tempRight = kInit; if (StrictIsValid (theSoundInfo)) { theErr = ASoundGetVolume (theSoundInfo, &tempLeft, &tempRight); if (theErr == noErr) { if ((tempLeft + leftVol) > kMaxVolume) leftVol = kMaxVolume - tempLeft; if ((tempRight + rightVol) > kMaxVolume) rightVol = kMaxVolume - tempRight; if ((tempLeft + leftVol) < kMinVolume) leftVol = -tempLeft; if ((tempRight + rightVol) < kMinVolume) rightVol = -tempRight; theErr = ASoundSetVolume (theSoundInfo, tempLeft+leftVol, tempRight+rightVol); } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundChangeVolume"); } return theErr; } /* Purpose: Gets the volume of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundGetVolume (SoundInfoPtr theSoundInfo, unsigned short *leftVol, unsigned short *rightVol) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {getVolumeCmd, kInit, kInit}; unsigned long theVol = kInit; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theCmd.param2 = (long)&theVol; theErr = SndDoImmediate(theSoundInfo->chan, &theCmd); if (theErr == noErr) { *leftVol = theVol & kLeftMask; *rightVol = theVol >> kSixteen; } } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetVolume"); } return theErr; } /* Purpose: Sets the volume of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundSetVolume (SoundInfoPtr theSoundInfo, unsigned short leftVol, unsigned short rightVol) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {volumeCmd, kInit, kInit}; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theCmd.param2 = (rightVol << kSixteen) | leftVol; theErr = SndDoImmediate(theSoundInfo->chan, &theCmd); } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundSetVolume"); } return theErr; } /* Purpose: Gets the rate multiplier of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundGetRateMul (SoundInfoPtr theSoundInfo, UnsignedFixed *theRateMul) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {getRateMultiplierCmd, kInit, kInit}; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theCmd.param2 = (long)theRateMul; theErr = SndDoImmediate(theSoundInfo->chan, &theCmd); } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundGetRateMul"); } return theErr; } /* Purpose: Gets the rate multiplier of the currently playing sound. Side Effects: None. */ /*-----------------------------------------------------------------------*/ OSErr ASoundSetRateMul (SoundInfoPtr theSoundInfo, UnsignedFixed theRateMul) /*-----------------------------------------------------------------------*/ { SndCommand theCmd = {rateMultiplierCmd, kInit, kInit}; OSErr theErr = noErr; if (StrictIsValid (theSoundInfo)) { theCmd.param2 = (long)theRateMul; theErr = SndDoImmediate(theSoundInfo->chan, &theCmd); } else { theErr = kNilPtrErr; } if (theErr != noErr) { DebugPrint ("\pError in ASoundSetRateMul"); } return theErr; }